﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Timers;
using System.Xml.Linq;
using System.Threading.Tasks;
using System.Configuration;

namespace Service
{
    public partial class Service : ServiceBase
    {
        private Timer timer = null;

        public Service()
        {
            InitializeComponent();
            try
            {
                if (!EventLog.SourceExists(this.ServiceName + "_log"))
                    EventLog.CreateEventSource(this.ServiceName, this.ServiceName + "_log");
            }
            catch { }

            serviceLog.Source = this.ServiceName + "_log";
            serviceLog.Log = this.ServiceName + "_log";
        }

        protected override void OnStart(string[] args)
        {
            var configurationFile = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "configuration.xml");
            var config = new ServiceConfig(configurationFile);

            serviceLog.WriteEntry(this.ServiceName + " запущен");
            Properties.Settings.Default.Reload();
            var ExecuteTimes = config.GetExecuteTimes().ToList();

            if (Properties.Settings.Default.ExecuteImmediately)
                ExecuteTimes.Add(DateTime.Now.AddMinutes(1).ToString("HH:mm"));

            timer = new Timer();
            timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
            timer.Interval = TimerUtil.GetNewTimerInterval(ExecuteTimes);
            timer.Enabled = true;
        }

        protected override void OnStop()
        {
            serviceLog.WriteEntry(this.ServiceName + " остановлен");
            timer.Stop();
            timer.Close();
        }

        private void OnElapsedTime(object source, ElapsedEventArgs e)
        {
            var configurationFile = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "configuration.xml");
            var config = new ServiceConfig(configurationFile);

            // Останавливаем таймей и стартуем выполнение выгрузки
            timer.Stop();
            Process();
            // Интервал таймера
            Properties.Settings.Default.Reload();
            timer.Interval = TimerUtil.GetNewTimerInterval(config.GetExecuteTimes());
            timer.Start();
        }

        private void Process()
        {
            try
            {
                string errMsg = "Запуск процедуры";
                serviceLog.WriteEntry(errMsg, EventLogEntryType.Information);

                var configurationFile = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "configuration.xml");
                var config = new ServiceConfig(configurationFile);

                string path = GetPath("cml");

                this.MakeCommerceML(path);
                this.SendCommerceML(path, config.GetBooleanValue("DeleteCommerceML"));                                
            }
            catch (Exception ex)
            {
                string errMsg = "При выполнении процедуры произошла ошибка " + ex.Message + "\n" + ex.StackTrace;
                serviceLog.WriteEntry(errMsg, EventLogEntryType.Error);
            }
        }

        private string GetPath(string type)
        {
            string subfolder = "";

            switch (type)
            {
                case "cml":
                    subfolder = "documents";
                    break;
                case "log":
                    subfolder = "log";
                    break;
                default:
                    break;
            }

            string path = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), subfolder);

            if (!System.IO.Directory.Exists(path))
            {
                System.IO.Directory.CreateDirectory(path);
            }

            return path;
        }

        private void MakeCommerceML(string path)
        {
            var configurationFile = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "configuration.xml");
            var config = new ServiceConfig(configurationFile);

            EDEX_1C2CommerceML.I1C context;

            context = Get1CContext();

            if (config.GetBooleanValue("1cUseJournal"))
            {
                var documents = context.GetDocumentsByPeriod(config.GetDateTimeValue("SyncDate"), DateTime.Today.AddDays(1));
                foreach (var document in documents)
                {
                    var commerceMLdocument = context.GetCommerceML(document);
                    this.ProcessCommerceML(path, commerceMLdocument);
                }
            }
            else
            {
                var documents = context.GetDocumentsByDay(config.GetNumericValue("SyncDay"));
                foreach (var document in documents)
                {
                    var commerceMLdocument = context.GetCommerceML(document);
                    this.ProcessCommerceML(path, commerceMLdocument);
                }
            }
        }

        private EDEX_1C2CommerceML.I1C Get1CContext()
        {
            var configurationFile = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "configuration.xml");
            var config = new ServiceConfig(configurationFile);

            EDEX_1C2CommerceML.I1C context;

            switch (config.GetValue("1cVersion"))
            {
                case "7.7":
                    context = new EDEX_1C2CommerceML._1Cv77(config.GetValue("1cConnectionString"));
                    break;
                case "8.1":
                    context = new EDEX_1C2CommerceML._1Cv81(config.GetValue("1cConnectionString"));
                    break;
                case "8.2":
                    context = new EDEX_1C2CommerceML._1Cv82(config.GetValue("1cConnectionString"));
                    break;
                default:
                    context = null;
                    break;
            }
            return context;
        }

        private void ProcessCommerceML(string path, CommerceML.Version205.КоммерческаяИнформация document)
        {
            var configurationFile = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "configuration.xml");
            var config = new ServiceConfig(configurationFile);

            var suppliers = config.GetSuppliers();
            var log = this.GetLog();

            var item = document.Items
                .Cast<CommerceML.Version205.Документ>()
                .FirstOrDefault();

            var fileName = System.IO.Path.Combine(path, item.Ид + ".xml");

            if (!log.Contains(item.Ид))
            {
                if (suppliers.Any())
                {
                    if (!item.Контрагенты.Any(r => suppliers.Select(n => n.Key).Contains(r.Ид)))
                        return;
                }

                using (var writeStream = new System.IO.StreamWriter(fileName))
                {
                    System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(CommerceML.Version205.КоммерческаяИнформация));
                    serializer.Serialize(writeStream, document);
                    writeStream.Close();
                }

                this.AddToLog(item.Ид);
            }
        }

        private async void SendCommerceML(string path, bool delete)
        {
            try
            {
                var configurationFile = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "configuration.xml");
                var config = new ServiceConfig(configurationFile);

                var edex = new EdexApiClient.EdexApiContext(
                    config.GetValue("ApiUri"),
                    config.GetValue("RootUri"), 
                    config.GetValue("EdexEmail"), 
                    config.GetValue("EdexPassword"));
                edex.ChangeCompany(config.GetValue("EdexCompanyKey"));

                foreach (var fileName in System.IO.Directory.GetFiles(path))
                {
                    try
                    {
                        using (var stream = new System.IO.FileStream(fileName, System.IO.FileMode.Open))
                        {
                            var result = await edex.UploadOutgoingInvoices(stream, "CommerceML.xml");
                            if (result.StatusCode != System.Net.HttpStatusCode.OK &&
                                result.StatusCode != System.Net.HttpStatusCode.NoContent)
                            {
                                var js = new System.Web.Script.Serialization.JavaScriptSerializer();
                                var data = js.Deserialize<EdexApiClient.ExceptionInfo>(result.Content.ReadAsStringAsync().Result);

                                throw new Exception(data.ExceptionMessage);
                            }

                            stream.Close();
                        }

                        if (delete) System.IO.File.Delete(fileName);
                    }
                    catch (Exception ex)
                    {
                        string errMsg = "Ошибка при отправке документа. " + ex.Message + "\n" + ex.StackTrace;
                        serviceLog.WriteEntry(errMsg, EventLogEntryType.Error);
                    }
                }
            }
            catch (Exception ex)
            {
                string errMsg = "Ошибка подключения к Edex " + ex.Message + "\n" + ex.StackTrace;
                serviceLog.WriteEntry(errMsg, EventLogEntryType.Error);
            }
        }

        private IEnumerable<string> GetLog()
        {
            var fileName = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "log.xml");

            if (!System.IO.File.Exists(fileName))
            {
                var document = new XDocument(
                    new XElement("logs"));

                document.Save(fileName);
            }

            return XDocument.Load(fileName)
                .Descendants("document")
                .Select(r => r.Attribute("id").Value);
        }

        private void AddToLog(string id)
        {
            var fileName = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "log.xml");

            var document = XDocument.Load(fileName);
            document.Root.Add(
                new XElement("log",
                    new XAttribute("date", DateTime.Now.ToString()),
                    new XElement("document", new XAttribute("id", id))));

            document.Save(fileName);
        }
    }
}
